#ifndef PYTHONINTERPRETER_H
#define PYTHONINTERPRETER_H

#include <pybind11/embed.h>
#include <pybind11/stl.h>

#include <unordered_map>
#include <string>
#include <iostream>
#include <filesystem>
#include <mutex>

namespace fs = std::filesystem;
namespace py = pybind11;

class PythonInterpreter {
public:

    static PythonInterpreter& instance() {
        static PythonInterpreter instance;
        return instance;
    }

    static void setPythonHome(const std::wstring& home) {
        Py_SetPythonHome(const_cast<wchar_t*>(home.c_str()));
    }

    PythonInterpreter() {
        _guard = std::make_unique<py::scoped_interpreter>();
    }

    ~PythonInterpreter() {
        _modules.clear();
        _guard.reset();
    }

    void add_search_path(const std::string& path) {
        auto sys = py::module_::import("sys");
        sys.attr("path").attr("append")(path.c_str());
    }

    void add_search_path(const fs::path& path) {
        if (!fs::exists(path)) {
            //throw std::runtime_error("Path does not exist: " + path.string());
            std::cout << "Path does not exist: " << path.string() << std::endl;
            return;
        }
        add_search_path(path.string());
    }

    template <typename... Args>
    py::object call_function(const std::string& module_name,
                                   const std::string& func_name,
                                   Args&&... args) {
        std::lock_guard<std::mutex> lock(_mtx);
        py::gil_scoped_acquire acquire;

        try {
            py::module_ module = load_module(module_name);
            if (module.is_none()) {
                return py::none();
            }
            return module.attr(func_name.c_str())(std::forward<Args>(args)...);
        }
        catch (const py::error_already_set& e) {
            std::cerr << "Python error: " << e.what() << std::endl;
            return py::none();
        }
        catch (const std::exception& e) {
            std::cerr << "C++ error: " << e.what() << std::endl;
            return py::none();
        }
    }

//protected:
    py::module_ load_module(const std::string& name) {
        //std::lock_guard<std::mutex> lock(_mtx);
        py::gil_scoped_acquire acquire;
        auto it = _modules.find(name);
        if (it != _modules.end()) {
            return it->second;
        }

        try
        {
            py::module_ module = py::module_::import(name.c_str());
            _modules[name] = module;
            return module;
        }
        catch (const py::error_already_set& e) {
            std::cerr << "module: "<< name <<"  load error: " << e.what() << std::endl;
            return py::module_();
        }
        catch(const std::exception& e)
        {
            std::cerr << "module: "<< name <<"  load error: " << e.what() << std::endl;
            return py::module_();
        }
    }

private:
    std::unique_ptr<py::scoped_interpreter> _guard;
    std::unordered_map<std::string, py::module_> _modules;
    std::mutex _mtx;
};

#define PYTHON_INTERPRETER PythonInterpreter::instance()

#endif